<?php

什么东西？
怎么用？


// ===============================================================



use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

经过查看源码，流程是这样的：
\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php
类中有方法
abstract class Illuminate\Support\Facades\Facade {
    public static function setFacadeApplication($app)
    {
        static::$app = $app;
    }
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
方法 setFacadeApplication() 调用是在：
\vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\RegisterFacades.php
    namespace Illuminate\Foundation\Bootstrap;

    use Illuminate\Foundation\AliasLoader;
    use Illuminate\Support\Facades\Facade;
    use Illuminate\Contracts\Foundation\Application;

    class RegisterFacades
    {
        /**
         * Bootstrap the given application.
         *
         * @param  \Illuminate\Contracts\Foundation\Application  $app
         * @return void
         */
        public function bootstrap(Application $app)
        {
            Facade::clearResolvedInstances();

            Facade::setFacadeApplication($app);

            AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
        }
    }
类 RegisterFacades，
    D:\project\study\study-php-laravel\laravel5331\vendor\composer\autoload_classmap.php (2 hits)
        Line 896:     'Illuminate\\Foundation\\Bootstrap\\RegisterFacades' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php',
    D:\project\study\study-php-laravel\laravel5331\vendor\composer\autoload_static.php (2 hits)
        Line 1189:         'Illuminate\\Foundation\\Bootstrap\\RegisterFacades' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php',
    D:\project\study\study-php-laravel\laravel5331\vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\RegisterFacades.php (1 hit)
        Line 9: class RegisterFacades
    D:\project\study\study-php-laravel\laravel5331\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php (1 hit)
        Line 62:         'Illuminate\Foundation\Bootstrap\RegisterFacades',
    D:\project\study\study-php-laravel\laravel5331\vendor\laravel\framework\src\Illuminate\Foundation\Console\Optimize\config.php (1 hit)
        Line 55:     $basePath.'/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php',
    D:\project\study\study-php-laravel\laravel5331\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php (1 hit)
        Line 41:         'Illuminate\Foundation\Bootstrap\RegisterFacades',
可以看到，排除 Console 使用和 composer 的映射之外，只在 \vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php 中使用了一次：
    protected $bootstrappers = [
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',// 就在这里；
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];// 这些类就是实实在在的类，可以直接实例化；

\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
    public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();

            $response = $this->sendRequestThroughRouter($request);// 这里；
        } catch (Exception $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->fire('kernel.handled', [$request, $response]);

        return $response;
    }
上面这个 handle() 方法就是在 \public\index.php 中使用的：
    $response = $kernel->handle(
        $request = Illuminate\Http\Request::capture()
    );

handle() 内调用 $this->sendRequestThroughRouter($request);
同一个文件中：
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();// 在这里；

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

sendRequestThroughRouter() 内调用 $this->bootstrap();
同一个文件中：
    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }

方法 $this->bootstrappers() 就是同一个文件中：
    protected function bootstrappers()
    {
        return $this->bootstrappers;// 正是上面那个数组；
    }

    在继续找下去之前，我们看下 $this->app 是什么？其实知道就是 \public\index.php 中的 $app，但还是要看看哪里赋值的。
    还是同一个文件中，构造方法如下：
        public function __construct(Application $app, Router $router)
        {
            $this->app = $app;
            $this->router = $router;

            $router->middlewarePriority = $this->middlewarePriority;

            foreach ($this->middlewareGroups as $key => $middleware) {
                $router->middlewareGroup($key, $middleware);
            }

            foreach ($this->routeMiddleware as $key => $middleware) {
                $router->middleware($key, $middleware);
            }
        }

    那么这类怎么实例化的呢？
    $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

    绑定则是在 \bootstrap\app.php
        $app->singleton(
            Illuminate\Contracts\Http\Kernel::class,
            App\Http\Kernel::class
        );

    之前已知，这种两个类名字符串的方式绑定的，会强制的绑定闭包，在闭包内new 实现类进行实例化；
    并且，在实例化时会把 $app 传递过去。 $app 就是容器了。

下面继续找。
$this->bootstrap() 内调用的是 
$this->app->bootstrapWith($this->bootstrappers());
那么到 \vendor\laravel\framework\src\Illuminate\Foundation\Application.php 内找方法
    public function bootstrapWith(array $bootstrappers)
    {
        echo "<pre>";
        print_r($bootstrappers);exit;
        // 在这里，正是上面的那个 $bootstrappers 数组。

        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

            $this->make($bootstrapper)->bootstrap($this);// 在这里；

            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }

bootstrapWith() 内的 foreach 循环中调用了
$this->make($bootstrapper)->bootstrap($this);

Illuminate\Foundation\Bootstrap\RegisterFacades 类中正有这个方法：
    public function bootstrap(Application $app)
    {
        Facade::clearResolvedInstances();

        Facade::setFacadeApplication($app);

        AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
    }

Facade 类就是  Illuminate\Support\Facades\Facade。
所以，凡是继承自  Illuminate\Support\Facades\Facade 的类，均有 static::$app = $app;


// ===============================================================


接下来看看使用的 Facade 做了什么？


use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

\vendor\laravel\framework\src\Illuminate\Support\Facades\Cache.php
    namespace Illuminate\Support\Facades;

    /**
     * @see \Illuminate\Cache\CacheManager
     * @see \Illuminate\Cache\Repository
     */
    class Cache extends Facade
    {
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'cache';
        }
    }

get() 方法不在 Cache 内中。
父类 Illuminate\Support\Facades\Facade 中也没有这个方法，但有魔术方法：
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }

可以看到是先实例化，然后调用指定的方法 get。

实例化的流程是：
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }
这个方法别重写了：
        protected static function getFacadeAccessor()
        {
            return 'cache';
        }

得到了 cache  字符串后：
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        return static::$resolvedInstance[$name] = static::$app[$name];
    }

此时不管是否已经解析过，就认为没有解析，则会执行
    return static::$resolvedInstance[$name] = static::$app[$name];
也就是 return static::$resolvedInstance['cache'] = static::$app['cache'];
此前已知，$app['cache']，就是借助于 ArrayAccess 的特性进行实例化，并放入容器中。那么这里，如果容器中已有，则直接返回，否则实例化。
    基于 $app->instances 和 $app->bindings。
那么就是 $app->aliases 中那么使用 cache 作为别名的类的实例。

\vendor\laravel\framework\src\Illuminate\Foundation\Application.php
    public function registerCoreContainerAliases()
    {
        $aliases = [
            'app'                  => ['Illuminate\Foundation\Application', 'Illuminate\Contracts\Container\Container', 'Illuminate\Contracts\Foundation\Application'],
            'auth'                 => ['Illuminate\Auth\AuthManager', 'Illuminate\Contracts\Auth\Factory'],
            'auth.driver'          => ['Illuminate\Contracts\Auth\Guard'],
            'blade.compiler'       => ['Illuminate\View\Compilers\BladeCompiler'],
            'cache'                => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'],
            ...
        ];
        ...
    }
这是要借助于 CacheServiceProvider 的。

在 \config\app.php 的 providers 数组中确实存在：
Illuminate\Cache\CacheServiceProvider::class,

Illuminate\Cache\CacheServiceProvider 则是
    namespace Illuminate\Cache;

    use Illuminate\Support\ServiceProvider;
    use Illuminate\Cache\Console\ClearCommand;

    class CacheServiceProvider extends ServiceProvider
    {
        /**
         * Indicates if loading of the provider is deferred.
         *
         * @var bool
         */
        protected $defer = true;

        /**
         * Register the service provider.
         *
         * @return void
         */
        public function register()
        {
            $this->app->singleton('cache', function ($app) {
                return new CacheManager($app);
            });

            $this->app->singleton('cache.store', function ($app) {
                return $app['cache']->driver();
            });

            $this->app->singleton('memcached.connector', function () {
                return new MemcachedConnector;
            });

            $this->registerCommands();
        }

确实在 $app->bindings 数组中绑定了 key 为 cache 的依赖。

所以 Cache::get('key');
实际上是 (new \Illuminate\Cache\CacheManager([...]))->get('key');


// ===============================================================


这样，我们就知道，Facade 到底是啥玩意了。

文档中说：
所有的 Laravel facades 都在 Illuminate\Support\Facades 这个命名空间下。
所以，我们能用的也就是这些东西了。
这些就是在 \config\app.php 中的数组：
    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        'Auth' => Illuminate\Support\Facades\Auth::class,
        'Blade' => Illuminate\Support\Facades\Blade::class,
        'Bus' => Illuminate\Support\Facades\Bus::class,
        'Cache' => Illuminate\Support\Facades\Cache::class,
        'Config' => Illuminate\Support\Facades\Config::class,
        'Cookie' => Illuminate\Support\Facades\Cookie::class,
        'Crypt' => Illuminate\Support\Facades\Crypt::class,
        'DB' => Illuminate\Support\Facades\DB::class,
        'Eloquent' => Illuminate\Database\Eloquent\Model::class,
        'Event' => Illuminate\Support\Facades\Event::class,
        'File' => Illuminate\Support\Facades\File::class,
        'Gate' => Illuminate\Support\Facades\Gate::class,
        'Hash' => Illuminate\Support\Facades\Hash::class,
        'Lang' => Illuminate\Support\Facades\Lang::class,
        'Log' => Illuminate\Support\Facades\Log::class,
        'Mail' => Illuminate\Support\Facades\Mail::class,
        'Notification' => Illuminate\Support\Facades\Notification::class,
        'Password' => Illuminate\Support\Facades\Password::class,
        'Queue' => Illuminate\Support\Facades\Queue::class,
        'Redirect' => Illuminate\Support\Facades\Redirect::class,
        'Redis' => Illuminate\Support\Facades\Redis::class,
        'Request' => Illuminate\Support\Facades\Request::class,
        'Response' => Illuminate\Support\Facades\Response::class,
        'Route' => Illuminate\Support\Facades\Route::class,
        'Schema' => Illuminate\Support\Facades\Schema::class,
        'Session' => Illuminate\Support\Facades\Session::class,
        'Storage' => Illuminate\Support\Facades\Storage::class,
        'URL' => Illuminate\Support\Facades\URL::class,
        'Validator' => Illuminate\Support\Facades\Validator::class,
        'View' => Illuminate\Support\Facades\View::class,

    ],
在 \routes\web.php、\routes\api.php、\routes\console.php 中，
直接使用 Route::get() 等来设置路由。
经过测试，在 \app\Http\Controllers\Web\TestController.php 中使用 Session::all() 则不行。
报错是：Fatal error: Class 'App\Http\Controllers\Web\Session' not found in ...
后来想到，路由配置文件中，没有使用命名空间，那么 Route 就是根命名空间。
于是，改成 \Session:all()，果然成功了。
所以，我在看到官方文档上示例
    use Illuminate\Support\Facades\Cache;

    Route::get('/cache', function () {
        return Cache::get('key');
    });
就觉得纳闷，如果这样用的话，那还 aliases 数组干什么。



另外，由于使用 Facade 需要从容器中解析出对象
    public static function getFacadeRoot()
    {
        // 这里的 static::getFacadeAccessor() 就是 key；
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

为了得到 key，就必须重写方法：
    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }
这是非常重要的。